package com.ywu.standard.gateway.handler.user;

import com.auth0.jwt.exceptions.TokenExpiredException;
import com.ywu.app.manage.spi.dto.YwuAppUserDto;
import com.ywu.app.manage.spi.entity.YwuAppEntity;
import com.ywu.app.manage.spi.spi.YwuAppSpi;
import com.ywu.boot.config.BootConfigAppConfig;
import com.ywu.boot.context.YwuContext;
import com.ywu.boot.context.YwuContextApp;
import com.ywu.boot.context.YwuContextHolder;
import com.ywu.boot.context.YwuContextUser;
import com.ywu.common.constant.CommonConstant;
import com.ywu.common.gson.GsonUtils;
import com.ywu.common.result.CommonResult;
import com.ywu.common.utils.AesUtils;
import com.ywu.standard.gateway.config.AppConfig;
import com.ywu.standard.gateway.handler.user.impl.UserHandlerInterface;
import com.ywu.standard.gateway.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.ResponseCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @ClassName UserAppInfoHandler
 * @Description TODO
 * @Author GroundDemo
 * @Date 2024/8/4 18:34
 * @Version 1.0
 **/
@Component
@Slf4j
@Order(5)
public class UserAppInfoHandler implements UserHandlerInterface {
    private static final String userAppHandlerCookieName = "currentAppInfoKey";

    private static final String userAppKeyName = "userAppInfo";

    private static final String USER_APP_HEADER_KEY = "X-Pix-Ywu-User-App";

    private static final String GUEST_ACCOUNT = "guest";

    private static final String YWU_GATEWAY_APP_INFO_REDIS_KEY = "YWU_GATEWAY_APP_INFO:gateway:";

    @Autowired
    private AppConfig appConfig;

    @Autowired
    private BootConfigAppConfig bootConfigAppConfig;

    @Autowired
    private YwuAppSpi ywuAppSpi;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public Object handle(ServerWebExchange exchange) {
        log.debug("UserAppInfoHandler begin");
        // 判断是否需要填充用户应用信息，如果是默认游客接口则默认放通
        if (YwuContextHolder.getCurrentContext().isFilterChain()) {
            log.info("UserRoleHandler isFilterChain is true");
            return exchange;
        }
        // 当前账号为游客，直接返回空的对象
        YwuContext currentContext = YwuContextHolder.getCurrentContext();
        if (GUEST_ACCOUNT.equals(currentContext.getYwuContextUser().getUserAccount())) {
            YwuContextApp ywuContextApp = new YwuContextApp();
            currentContext.setYwuContextApp(ywuContextApp);
        }
        // 获取对应的cookie
        MultiValueMap<String, HttpCookie> cookies = exchange.getRequest().getCookies();
        List<HttpCookie> accessToken = cookies.get(userAppHandlerCookieName);
        // 如果请求头中的Cookie不存在，则返回异常
        if (CollectionUtils.isEmpty(accessToken) || accessToken.size() > 1) {
            return doNoCookieKey(exchange);
        } else {
            return doHasCookieKey(exchange, accessToken.get(0));
        }
    }

    /**
    * @Author GroundDemo
    * @Description 处理请求头中不存在cookie时的情况
    * @Date 18:44 2024/8/4
    * @Param []
    * @return java.lang.Object
    **/
    private Object doNoCookieKey(ServerWebExchange exchange) {
        List<YwuAppEntity> data = null;
        YwuContextUser ywuContextUser = YwuContextHolder.getCurrentContext().getYwuContextUser();
        // 优先使用缓存
        String redisInfo = redisTemplate.opsForValue().get(YWU_GATEWAY_APP_INFO_REDIS_KEY + ywuContextUser.getUserAccount());
        if (Objects.nonNull(redisInfo)) {
            log.debug("gateway query user app info using redis cache");
            data = GsonUtils.deserializeList(redisInfo, YwuAppEntity.class);
        } else {
            // 查询当前用户下所有的应用列表
            YwuAppUserDto ywuAppUserDto = new YwuAppUserDto();
            ywuAppUserDto.setUserId(ywuContextUser.getId());
            // 支持异步调用
            CompletableFuture<CommonResult<List<YwuAppEntity>>> commonResultCompletableFuture =
                    CompletableFuture.supplyAsync(() -> ywuAppSpi.queryAppInfoByUser(ywuAppUserDto));
            CommonResult<List<YwuAppEntity>> objectCommonResult;
            try {
                objectCommonResult = commonResultCompletableFuture.get();
            } catch (InterruptedException | ExecutionException e) {
                log.error("doNoCookieKey handleUserAppInfo error {}", e.getMessage());
                return CommonResult.error().setCode(CommonConstant.USER_APP_QUERY_INFO_ERROR).setErrorCode(CommonConstant.USER_APP_QUERY_INFO_ERROR_MESSAGE)
                        .setMessage("应用查询异常!");
            }
            data = objectCommonResult.getData();
            // 添加缓存
            redisTemplate.opsForValue().set(YWU_GATEWAY_APP_INFO_REDIS_KEY + ywuContextUser.getUserAccount(), GsonUtils.serialize(data));
            redisTemplate.expire(YWU_GATEWAY_APP_INFO_REDIS_KEY + ywuContextUser.getUserAccount(), 1, TimeUnit.HOURS);
        }
        // 判断查询出的结果是否为空
        if (CollectionUtils.isEmpty(data)) {
            // 如果为空，直接放行
            return exchange;
        }
        // 解密Token 的key
        String decryptTokenKey;
        try {
            decryptTokenKey = AesUtils.decrypt(bootConfigAppConfig.getTokenKey(), bootConfigAppConfig.getSecretKey());
        } catch (Exception e) {
            log.error("doNoCookieKey decryptTokenKey error {}", e.getMessage());
            return CommonResult.error().setCode(CommonConstant.USER_APP_QUERY_INFO_ERROR).setErrorCode("do.has.cookie.key.decrypt.error")
                    .setMessage("秘钥解密异常!");
        }
        // 以列表的第一个应用作为默认值存入上下文及cookie中
        YwuAppEntity ywuAppEntity = data.get(0);
        YwuContextApp ywuContextApp = new YwuContextApp();
        BeanUtils.copyProperties(ywuAppEntity, ywuContextApp);
        YwuContext currentContext = YwuContextHolder.getCurrentContext();
        currentContext.setYwuContextApp(ywuContextApp);
        String appInfoToken = JwtUtils.createAppInfoToken(currentContext.getYwuContextUser().getUserAccount(),
                GsonUtils.serialize(ywuContextApp),
                decryptTokenKey, appConfig.getAccessExpire());
        ResponseCookie appInfoTokenCookie = ResponseCookie.from(userAppHandlerCookieName, appInfoToken).path("/").build();
        exchange.getResponse().addCookie(appInfoTokenCookie);
        String encrypt;
        try {
            encrypt = AesUtils.encrypt(GsonUtils.serialize(ywuContextApp), bootConfigAppConfig.getSecretKey());
        } catch (Exception e) {
            log.error(" doNoCookieKey handleUserApp encrypt error {}", e.getMessage());
            return CommonResult.error().setCode(CommonConstant.USER_APP_QUERY_INFO_ERROR).setErrorCode(CommonConstant.USER_APP_QUERY_INFO_ERROR_MESSAGE)
                    .setMessage("用户应用信息加密异常!");
        }
        ServerHttpRequest mutatedUserRoleRequest = exchange.getRequest().mutate().header(USER_APP_HEADER_KEY, encrypt).build();
        return exchange.mutate().request(mutatedUserRoleRequest).build();
    }
    
    /**
    * @Author GroundDemo
    * @Description 处理请求头中存在cookie时的情况
    * @Date 18:45 2024/8/4
    * @Param [exchange, cookie]
    * @return java.lang.Object
    **/
    private Object doHasCookieKey(ServerWebExchange exchange, HttpCookie cookie) {
        // 解密Token 的key
        String decryptTokenKey;
        try {
            decryptTokenKey = AesUtils.decrypt(bootConfigAppConfig.getTokenKey(), bootConfigAppConfig.getSecretKey());
        } catch (Exception e) {
            log.error("doHasCookieKey decryptTokenKey error {}", e.getMessage());
            return CommonResult.error().setCode(CommonConstant.USER_APP_QUERY_INFO_ERROR).setErrorCode("do.has.cookie.key.decrypt.error")
                    .setMessage("秘钥解密异常!");
        }
        // 根据解密后的token key解析Token
        try {
            Map<String, String> tokenInfoMap = JwtUtils.decodeToken(cookie.getValue(), decryptTokenKey);
            String userAppInfo = tokenInfoMap.get(userAppKeyName);
            // 兼容token加密后的引号转义
            userAppInfo = userAppInfo.replaceAll("\\\\", "");
            log.debug("doHasCookieKey userAppInfo {}", userAppInfo);
            // 反序列化为app对象
            YwuContextApp deserializeApp = GsonUtils.deserialize(userAppInfo, YwuContextApp.class);
            log.debug("doHasCookieKey deserializeApp {}", deserializeApp);
            // 根据当前用户及当前appId查询应用与用户的授权关系
            YwuContextUser ywuContextUser = YwuContextHolder.getCurrentContext().getYwuContextUser();
            // 优先使用缓存
            List<YwuAppEntity> data = null;
            String redisInfo = redisTemplate.opsForValue().get(YWU_GATEWAY_APP_INFO_REDIS_KEY + ywuContextUser.getUserAccount());
            if (Objects.nonNull(redisInfo)) {
                log.debug("gateway query user app info using redis cache");
                data = GsonUtils.deserializeList(redisInfo, YwuAppEntity.class);
            } else {
                YwuAppUserDto ywuAppUserDto = new YwuAppUserDto();
                ywuAppUserDto.setUserId(ywuContextUser.getId());
                // 支持异步调用
                CompletableFuture<CommonResult<List<YwuAppEntity>>> commonResultCompletableFuture =
                        CompletableFuture.supplyAsync(() -> ywuAppSpi.queryAppInfoByUser(ywuAppUserDto));
                CommonResult<List<YwuAppEntity>> objectCommonResult;
                try {
                    objectCommonResult = commonResultCompletableFuture.get();
                } catch (InterruptedException | ExecutionException e) {
                    log.error("doHasCookieKey handleUserAppInfo error {}", e.getMessage());
                    return CommonResult.error().setCode(CommonConstant.USER_APP_QUERY_INFO_ERROR).setErrorCode(CommonConstant.USER_APP_QUERY_INFO_ERROR_MESSAGE)
                            .setMessage("应用信息查询异常!");
                }
                data = objectCommonResult.getData();
                // 添加缓存
                redisTemplate.opsForValue().set(YWU_GATEWAY_APP_INFO_REDIS_KEY + ywuContextUser.getUserAccount(), GsonUtils.serialize(data));
                redisTemplate.expire(YWU_GATEWAY_APP_INFO_REDIS_KEY + ywuContextUser.getUserAccount(), 1, TimeUnit.HOURS);
            }
            // 判断是否存在用户与角色的授权
            if (!checkIsPermission(data, deserializeApp)) {
                // 删除cookie，并返回异常信息
                ResponseCookie appInfoTokenCookie = ResponseCookie.from(userAppHandlerCookieName, "").path("/")
                                .maxAge(0).build();
                exchange.getResponse().addCookie(appInfoTokenCookie);
                return CommonResult.error().setCode(CommonConstant.USER_APP_QUERY_INFO_ERROR).setErrorCode(CommonConstant.USER_APP_QUERY_INFO_ERROR_MESSAGE)
                        .setMessage("该应用用户无权限!");
            } else {
                // 将用户应用信息设置到上下文中
                YwuContextHolder.getCurrentContext().setYwuContextApp(deserializeApp);
                String encrypt;
                try {
                    encrypt = AesUtils.encrypt(userAppInfo, bootConfigAppConfig.getSecretKey());
                } catch (Exception e) {
                    log.error(" doHasCookieKey handleUserApp encrypt error {}", e.getMessage());
                    return CommonResult.error().setCode(CommonConstant.USER_APP_QUERY_INFO_ERROR).setErrorCode(CommonConstant.USER_APP_QUERY_INFO_ERROR_MESSAGE)
                            .setMessage("用户应用信息加密异常!");
                }
                ServerHttpRequest mutatedUserRoleRequest = exchange.getRequest().mutate().header(USER_APP_HEADER_KEY, encrypt).build();
                return exchange.mutate().request(mutatedUserRoleRequest).build();
            }
        } catch (TokenExpiredException e) {
            return CommonResult.error().setCode(CommonConstant.USER_APP_QUERY_INFO_ERROR).setErrorCode(CommonConstant.USER_APP_QUERY_INFO_ERROR_MESSAGE)
                    .setMessage("用户应用信息解析异常!");
        }
    }

    /**
    * @Author GroundDemo
    * @Description 判断是否存在用户与角色的授权
    * @Date 19:13 2024/8/4
    * @Param [data, deserializeApp]
    * @return boolean
    **/
    private boolean checkIsPermission(List<YwuAppEntity> data, YwuContextApp deserializeApp) {
        if (CollectionUtils.isEmpty(data)) {
            return false;
        }
        List<YwuAppEntity> collect = data.stream().filter(res -> StringUtils.equals(res.getAppId(),
                deserializeApp.getAppId())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(collect)) {
            return false;
        }
        return true;
    }
}
